
#include <BLabel>
#include <BButton>
#include <BVBoxLayout>
#include <BHBoxLayout>
#include <BMessageBox>

#include "CheckDialog.h"

CheckDialog::CheckDialog(BWidget* parent) : BDialog(parent)
{
	BHBoxLayout* splitLayout = new BHBoxLayout();
	splitLayout->setSplitable(true);
	splitLayout->setSpacing(5);
	{
		_treeWidget = new BTreeWidget();

		BWidget* rightWidget = new BWidget();
		{
			_titleLabel = new BLabel();

			_attribEdit = new BTextEdit();

			_textEdit = new BTextEdit();
			_textEdit->setPerch(Part_Top, 32);
			_textEdit->setTitle("Comment");

			BVBoxLayout* vlayout = new BVBoxLayout(rightWidget);
			vlayout->addWidget(_titleLabel, 0);
			vlayout->addWidget(_attribEdit);
			vlayout->addWidget(_textEdit);
		}

		splitLayout->addWidget(_treeWidget, 1);
		splitLayout->addWidget(rightWidget, 1);
	}

	BHBoxLayout* hlayout = new BHBoxLayout(this);
	hlayout->addLayout(splitLayout);

	BHBoxLayout* buttonLayout = new BHBoxLayout();
	{
		_saveButton = new BButton("save");
		_cancelButton = new BButton("cancel");

		buttonLayout->addSpring(10);
		buttonLayout->addWidget(_saveButton);
		buttonLayout->addWidget(_cancelButton);
	}
	this->setLayout(buttonLayout, Part_Bottom);

	connect(_treeWidget, Signal_ItemSelected, &CheckDialog::slotItemSelected);
	connect(_saveButton, Signal_Clicked, &CheckDialog::slotSave);
	connect(_cancelButton, Signal_Clicked, &CheckDialog::slotCancel);
}
CheckDialog::~CheckDialog()
{

}

bool CheckDialog::openHeadFile(const BString& fileName)
{
	_fileName = fileName;

	BFile file(fileName);
	if (!file.open(IO_Read))
		return false;

	_headRoot = new BXmlNode("html");
	_headRoot->setFormat("<>\n</");
	BXmlNode* headNode = _headRoot->addChild("head");
	headNode->setFormat("<>\n</>\n");
	{
		BXmlNode* metaNode = headNode->addChild("meta");
		metaNode->setAttrib("charset", "utf-8");
		metaNode->setFormat("</>\n");

		BXmlNode* titleNode = headNode->addChild("title");
		titleNode->setText(fileName.name());
		titleNode->setFormat("</>\n");

		BXmlNode* linkNode = headNode->addChild("link");
		linkNode->setAttrib("rel", "stylesheet");
		linkNode->setAttrib("type", "text/css");
		linkNode->setAttrib("href", "../style.css");
	}

	BXmlNode* body = _headRoot->addChild("body");
	body->setFormat("<>\n</>\n");
	BString text;
	file.read(text);

	int pos = 0;
	BString temp_text;
	BString line;
	BStringList words;
	while (pos < text.size())
	{
		pos = text.line(line, pos);
		if (line.contain("template"))
		{
			temp_text = line;
			continue;
		}
		if (line.contain("enum"))
		{
			BXmlNode* table = createEnumTable(text, pos);

			BXmlNode* divNode = body->addChild("div");
			divNode->setFormat("/>\n");
			divNode->setAttrib("id", table->text());

			BXmlNode* titleNode = divNode->addChild("h3");
			titleNode->setText(BString("enum ") + table->text());
		
			divNode->addChild(table);
			continue;
		}
		if (!line.contain("class") || line.contain(';'))
		{
			temp_text.reset();
			continue;
		}
		line.remove("class");
		line.remove("BWEAPI");
		line.simplify();
		if (line.empty())
			continue;
		if (0 == line.words(words))
			continue;

		BString class_name = words[0];

		int inherits_pos = text.find(" : public ");
		BString class_inherits = text.word(inherits_pos + 10);

		BXmlNode* class_name_node = new BXmlNode("h1");
		class_name_node->setText(class_name);
		class_name_node->setFormat("/>\n");
		body->addChild(class_name_node);

		if (class_inherits.size())
		{
			BXmlNode* inherits_node = body->addChild("p");
			inherits_node->setText("Inherites from ");
			inherits_node->setFormat("/>\n");

			BXmlNode* linkNode = inherits_node->addChild("a");
			linkNode->setText(class_inherits);
			class_inherits.append(".html");
			linkNode->setAttrib("href", class_inherits);
		}

		break;
	}

	BXmlNodeHolder publicTable = new BXmlNode("table");
	{//try collect public members

		pos = text.find("public:");
		pos = text.line(line, pos);

		collectMembers(publicTable, text, pos);
		if (publicTable->childCount())
		{
			publicTable->setFormat("/>\n");
			publicTable->setAttrib("id", "public");

			BXmlNode* titleNode = new BXmlNode("h2");
			titleNode->setText("public");
			titleNode->setFormat("/>\n");
			body->addChild(titleNode);

			body->addChild(publicTable);
		}
	}

	BXmlNodeHolder protectedTable = new BXmlNode("table");
	{//try collect protected members

		pos = text.find("protected:");
		pos = text.line(line, pos);

		collectMembers(protectedTable, text, pos);
		if (protectedTable->childCount())
		{
			protectedTable->setFormat("/>\n");
			protectedTable->setAttrib("id", "protected");

			BXmlNode* titleNode = new BXmlNode("h2");
			titleNode->setText("protected");
			titleNode->setFormat("/>\n");
			body->addChild(titleNode);

			body->addChild(protectedTable);
		}
	}

	//Detail Description
	BXmlNode* descNode = body->addChild("div");
	descNode->setAttrib("id", "desc");
	descNode->setFormat("<>\n</>\n");
	{
		BXmlNode* desc_title = descNode->addChild("h2");
		desc_title->setText("Detailed Description : ");
		desc_title->setFormat("/>\n");
	}

	BXmlNode* functionDocument = body->addChild("h2");
	functionDocument->setAttrib("id", "docs");
	functionDocument->setText("Member Documentation");
	functionDocument->setFormat("</>\n");

	if (publicTable->childCount())
	{
		for (int i = 0; i < publicTable->childCount(); i++)
		{
			BXmlNode* rowNode = publicTable->child(i);
			BXmlNode* linkNode = rowNode->find("a");
			const char* idStr = linkNode->attrib("href").cstr() + 1;

			BXmlNode* divNode = body->addChild("div");
			divNode->setAttrib("id", idStr);
			divNode->setFormat("<>\n</>\n");

			BXmlNode* titleNode = divNode->addChild("h3");
			titleNode->setFormat("/>\n");

			BXmlNode* table = linkNode->child("table");
			if (table)
				divNode->addChild(table);
			
			titleNode->setText(rowNode->plainText());
		}
	}

	if (protectedTable->childCount())
	{
		for (int i = 0; i < protectedTable->childCount(); i++)
		{
			BXmlNode* rowNode = protectedTable->child(i);

			BXmlNode* linkNode = rowNode->find("a");
			const char* idStr = linkNode->attrib("href").cstr() + 1;

			BXmlNode* divNode = body->addChild("div");
			divNode->setAttrib("id", idStr);
			divNode->setFormat("<>\n />\n");

			BXmlNode* methodTitle = divNode->addChild("h3");
			methodTitle->setAttrib("id", idStr);
			methodTitle->setText(rowNode->plainText());
			methodTitle->setFormat("/>\n");
		}
	}

	_treeWidget->clearItems();

	BTreeItem* descItem = _treeWidget->addTopItem("Description");
	descItem->setAsset(descNode);

	if (publicTable->childCount())
	{
		BTreeItem* publicItem = _treeWidget->addTopItem("public");
		for (int i = 0; i < publicTable->childCount(); i++)
		{
			BXmlNode* rowNode = publicTable->child(i);
			BXmlNode* linkNode = rowNode->find("a");
			if (!linkNode)
				continue;
			const BString& href = linkNode->attrib("href");
			BString desc("div:id=\"");
			desc.append(href, 1);
			desc.append('\"');
			BXmlNode* divNode = body->locate(desc);
			BTreeItem* childItem = publicItem->addChild(rowNode->plainText());
			childItem->setAsset(divNode);
		}
	}

	if (protectedTable->childCount())
	{
		BTreeItem* protectedItem = _treeWidget->addTopItem("protected");
		for (int i = 0; i < protectedTable->childCount(); i++)
		{
			BXmlNode* rowNode = protectedTable->child(i);
			BXmlNode* linkNode = rowNode->find("a");
			BString desc = linkNode->attrib("href");
			desc.replace('#', "div:id=\"");
			desc.append('\"');

			BXmlNode* divNode = body->locate(desc);

			BTreeItem* childItem = protectedItem->addChild(rowNode->plainText());
			childItem->setAsset(divNode);
		}
	}

	return true;
}
bool CheckDialog::loadHtmlFile(const BString& htmlFileName)
{
	_htmlFileName = htmlFileName;
	BFile file(_htmlFileName);
	if (!file.open(IO_Read))
		return false;

	BXmlDocument document;
	BXmlNode* htmlRoot = document.read(file);
	if (!htmlRoot)
		return false;
	
	BTreeItem* descItem = _treeWidget->topItem("Description");
	BXmlNode* descNode = descItem->asset();
	BXmlNode* htmlDescNode = htmlRoot->locate("body/div:id=\"desc\"");
	if (descItem && descNode && htmlDescNode)
	{
		for (int i = 0; i < htmlDescNode->childCount(); i++)
		{
			BXmlNode* child = htmlDescNode->child(i);
			if (child->name() != "h2")
			{
				descNode->addChild(child);
				i--;
			}
		}
	}

	BTreeItem* publicItem = _treeWidget->topItem("public");
	if (publicItem)
	{
		for (int i = 0; i < publicItem->childCount(); i++)
		{
			BTreeItem* methodItem = publicItem->child(i);
			BXmlNode* docNode = methodItem->asset();

			const BString& funcName = docNode->attrib("id");
			BString desc("body/div:id=\"");
			desc << funcName << '\"';

			BXmlNode* node = htmlRoot->locate(desc);
			if (node)
			{
				for (int i = 0; i < node->childCount(); i++)
				{
					BXmlNode* child = node->child(i);
					if (child->name() == "h3")
						continue;
					if (child->name() == "table" && child->attrib("class") == "enum")
					{
						BXmlNode* table = docNode->child("table:class=enum");
						for (int r = 0; r < table->childCount(); r++)
						{
							BXmlNode* tr = table->child(r);
							BString keyText = tr->child(0)->text();
							keyText.prepend("td@");
							if (BXmlNode* keyNode = child->find(keyText))
							{
								tr->setChild(1, keyNode->next());
							}
						}
					}
					else
					{
						docNode->addChild(child);
						i--;
					}
				}
			}
		}
	}

	BTreeItem* protectedItem = _treeWidget->topItem("protected");
	if (protectedItem)
	{
		for (int i = 0; i < protectedItem->childCount(); i++)
		{
			BTreeItem* methodItem = protectedItem->child(i);
			BXmlNode* docNode = methodItem->asset();

			const BString& funcName = docNode->attrib("id");
			BString desc("body/div:id=\"");
			desc << funcName << '\"';

			BXmlNode* node = htmlRoot->locate(desc);
			if (node)
			{
				for (int i = 0; i < node->childCount(); i++)
				{
					BXmlNode* child = node->child(i);
					if (child->name() == "h3")
						continue;
					docNode->addChild(child);
					i--;
				}
			}
		}
	}

	return true;
}
bool CheckDialog::save()
{
	BFile file(_htmlFileName);
	if (!file.open(IO_Write))
		return false;

	BXmlDocument document;
	bool ret = document.write(file, _headRoot);
	if (!ret)
		return false;

	if (this->title().contain('*'))
	{
		BString title = this->title();
		title.remove('*');
		this->setTitle(title);
	}
	return true;
}

int CheckDialog::strideBlock(const BString& text, int pos)
{
	int depth = 0;
	int level = 0;
	while (pos < text.size())
	{
		if (text[pos] == '{')
		{
			level++;
			depth++;
		}
		if (text[pos] == '}')
			level--;
		if (text[pos] == '\n' && depth > 0 && level == 0)
			return pos + 1;
		pos++;
	}
	return pos;
}

void CheckDialog::collectMembers(BXmlNode* table, const BString& text, int& pos)
{
	BString line;
	BString href;
	BString name;
	BString params;
	while (pos < text.size())
	{
		pos = text.line(line, pos);
		if (line.contain("protected:"))
			break;
		if (line.contain("private:"))
			break;
		if (line.contain("};"))
			break;
		if (line.contain("public:"))
			continue;
		if (line.contain("template"))
			continue;
		if (line.contain("typedef"))
			continue;
		if (line.contain("//"))
		{
			line.trimmed();
			if (line.beginWith("//"))
				continue;
			continue;
		}
		if (line.contain("enum"))
		{
			BXmlNode* enumTable = createEnumTable(text, pos);

			BXmlNode* rowNode = table->addChild("tr");
			rowNode->setFormat("/>\n");

			BXmlNode* td0 = rowNode->addChild("td");
			td0->setAttrib("align", "right");
			td0->setText("enum ");

			BXmlNode* td1 = rowNode->addChild("td");
			BXmlNode* link = td1->addChild("a");
			link->setAttrib("href", BString('#') + enumTable->text());
			link->setText(enumTable->text());
			link->addChild(enumTable);

			continue;
		}
		if (line.contain("class"))
		{
			line.remove("class");
			line.trimmed();

			BXmlNode* rowNode = table->addChild("tr");
			rowNode->setFormat("/>\n");

			BXmlNode* td0 = rowNode->addChild("td");
			td0->setAttrib("align", "right");
			td0->setText("class ");

			BXmlNode* td1 = rowNode->addChild("td");
			BXmlNode* link = td1->addChild("a");
			link->setAttrib("href", BString('#') + line);
			link->setText(line);

			pos = strideBlock(text, pos);

			continue;
		}
		line.simplify();
		line.trimmed();
		if (line.endWith(')') || line.endWith("const"))
		{
			pos = strideBlock(text, pos);
			line.append(';');
		}
		if (line.empty())
			continue;

		int name_end = line.find('(');
		int name_begin = line.find("operator");
		if (name_begin == line.size())
			name_begin = name_end - 1;
		while (name_begin > 0)
		{
			if (isspace(line[name_begin]))
			{
				name_begin++;
				break;
			}
			name_begin--;
		}
		line.substr(name, name_begin, name_end);

		int param_begin = name_end + 1;
		int param_end = line.rfind(')');
		line.substr(params, param_begin, param_end);
		if (params.size())
		{
			if (params.contain(','))
			{
				BStringList words = params.split(',');
				params.reset();
				for (int i = 0; i < words.size(); i++)
				{
					BString& word = words[i];
					int equal_pos = word.find('=');
					if (equal_pos < word.size())
					{
						word.remove(equal_pos, -1);
						word.trimmed();
					}
					int space_pos = word.rfind(' ');
					word.remove(space_pos, -1);
					params.append(word);
					params.append(',');
				}
				params -= ',';
			}
			else
			{
				int equal_pos = params.find('=');
				if (equal_pos < params.size())
				{
					params.remove(equal_pos, -1);
					params.trimmed();
				}
				int space_pos = params.rfind(' ');
				params.remove(space_pos, -1);
			}
		}

		BXmlNode* rowNode = table->addChild("tr");
		rowNode->setFormat("/>\n");

		BXmlNode* node0 = rowNode->addChild("td");
		node0->setAttrib("align", "right");
		node0->addText(line.substr(0, name_begin));

		BXmlNode* node1 = rowNode->addChild("td");
		{
			href.reset();
			href << '#' << name << '(' << params << ')';
			BXmlNode* method_link = node1->addChild("a");
			method_link->setText(name);
			method_link->setAttrib("href", href);
			BString text = line.substr(name_end);
			text.replace("<", "&lt");
			text.replace(">", "&gt");
			node1->addText(text);
		}
	}
}
BXmlNode* CheckDialog::createEnumTable(const BString& text, int& pos)
{
	BString str;
	text.line(str, pos - 2);
	str.remove("enum");
	str.trimmed();

	BXmlNode* table = new BXmlNode("table");
	table->setFormat("\n<\n</");
	table->setAttrib("class", "enum");
	table->setText(str);

	pos = text.find('{', pos);
	pos++;
	int stop = text.find('}', pos);
	int value = 0;
	str.reset();
	while (pos <= stop)
	{
		char ch = text[pos];
		if (ch == ',' || ch == '}')
		{
			str.trimmed();
			BXmlNode* tr = table->addChild("tr");
			tr->setFormat("\n<");
			BXmlNode* td0 = tr->addChild("td");
			BXmlNode* td1 = tr->addChild("td", "desc");
			if (str.contain('='))
			{
				BStringList words;
				if (2 == str.split(words, '='))
				{
					words[0].trimmed();
					td0->setText(words[0]);
				}
			}
			else
			{
				td0->setText(str);
			}
			str.reset();
		}
		else
		{
			str.append(ch);
		}
		pos++;
	}
	pos = text.line(str, pos);
	return table;
}

void CheckDialog::slotItemSelected(BObject* object, const BValue& value)
{
	BTreeItem* item = value;
	if (item)
	{
		BXmlNode* node = item->asset();
		if (node)
		{
			BString title;
			BString text;
			for (int i = 0; i < node->childCount(); i++)
			{
				BXmlNode* child = node->child(i);
				if (child->name().contain('h'))
				{
					title.append(child->plainText());
					continue;
				}
				text.append(child->plainText());
				text.append('\n');
			}
			_titleLabel->setText(title);
			_textEdit->setText(text);

			BString attribText;
			const BMap<BString, BString>& attribSet = node->attribSet();
			for (auto it = attribSet.begin(); it.valid(); ++it)
			{
				attribText << it.key() << '=' << it.value() << '\n';
			}
			_attribEdit->setText(attribText);
		}
		else
		{
			_titleLabel->setText(BString());
			_attribEdit->setText(BString());
			_textEdit->setText(BString());
		}
	}
}
void CheckDialog::slotSave()
{
	if (this->save())
	{
		this->close(1);
		return;
	}
	this->close(0);
	BMessageBox::Warning(this, "Warning", "Save html file failed.");
}
void CheckDialog::slotCancel()
{
	this->close(0);
}

void CheckDialog::updateEvent(const BEvent& event)
{

}
